#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>  // 补充fcntl.h头文件，定义信号量使用时的O_CREAT等宏
// 设置缓冲区大小
#define BUFFER_SIZE 5
// 生产/消费的产品数量
#define ITEM_COUNT 10
// 设置共享缓冲区结构体
typedef struct {
    int buffer[BUFFER_SIZE];  // 缓冲区数组
    int in;                   // 下一个放入产品的位置
    int out;                  // 下一个取出产品的位置
} SharedBuffer;
// 设置共享内存的键值（仅共享内存使用）
#define SHM_KEY 4321
int main() {
int shmid; //共享内存标识符（Shared Memory ID）    
SharedBuffer *shared_buf;
    sem_t *empty, *full, *mutex;
    pid_t pid;

    // 创建共享内存,IPC_CREAT如果SHM_KEY对应的共享内存不存在，则创建// 一块新的,0666表示可读可写
    shmid = shmget(SHM_KEY, sizeof(SharedBuffer), IPC_CREAT | 0666);
    if (shmid == -1) {
        perror("shmget failed");
        exit(EXIT_FAILURE);
    }
    // 连接共享内存到进程地址空间，NULL表示让系统自动选择共享内存挂载//到进程地址空间的位置，0 表示使用默认方式（可读可写），shmat 函数的返//回值是void*类型（通用指针）
    shared_buf = (SharedBuffer *)shmat(shmid, NULL, 0);
    if (shared_buf == (void *)-1) {
        perror("shmat failed");
        exit(EXIT_FAILURE);
    }

    // 初始化缓冲区
    shared_buf->in = 0;
    shared_buf->out = 0;

// 创建命名信号量（删除冗余的键值定义，直接使用路径名）
//O_CREAT："Open Create"（创建打开),O_EXCL："Open Exclusive"(独占打开)。
//仅与 O_CREAT 配合使用，当信号量已存在时，sem_open会返回错误
//（SEM_FAILED） empty信号量：表示空缓冲区数量，初始值为BUFFER_SIZE
// full信号量：表示满缓冲区数量，初始值为0
    // mutex信号量：保证互斥访问，初始值为1
    empty = sem_open("/empty_sem", O_CREAT | O_EXCL, 0666, BUFFER_SIZE);
    full = sem_open("/full_sem", O_CREAT | O_EXCL, 0666, 0);
    mutex = sem_open("/mutex_sem", O_CREAT | O_EXCL, 0666, 1);

    // 处理信号量已存在的情况（避免程序重启时因残留信号量报错）
    if (empty == SEM_FAILED || full == SEM_FAILED || mutex == SEM_FAILED) {
        // 若信号量已存在，sem_unlink是用于删除命名信号量的函数
        sem_unlink("/empty_sem");
        sem_unlink("/full_sem");
        sem_unlink("/mutex_sem");
        empty = sem_open("/empty_sem", O_CREAT, 0666, BUFFER_SIZE);
        full = sem_open("/full_sem", O_CREAT, 0666, 0);
        mutex = sem_open("/mutex_sem", O_CREAT, 0666, 1);
        if (empty == SEM_FAILED || full == SEM_FAILED || mutex == SEM_FAILED) {
            perror("sem_open failed");
            exit(EXIT_FAILURE);
        }
    }

    // 创建子进程
    pid = fork();
    if (pid == -1) {
        perror("fork failed");
        exit(EXIT_FAILURE);
    }
    if (pid == 0) {  // 子进程 - 消费者
        int item, i;   
        for (i = 0; i < ITEM_COUNT; i++) {
            // 等待有产品可以消费（full > 0）
            sem_wait(full);
           // printf("消费者: 被唤醒，开始取产品\n"); 
            // 等待获取互斥锁
            sem_wait(mutex);
            // 从缓冲区取出产品
            item = shared_buf->buffer[shared_buf->out];
            printf("消费者: 取出产品 %d (位置 %d)\n", item, shared_buf->out);
            shared_buf->out = (shared_buf->out + 1) % BUFFER_SIZE;
            // 释放互斥锁
            sem_post(mutex);
            // 增加空缓冲区数量
            sem_post(empty);
            // 模拟消费时间
            sleep(rand() % 2);
        }
        // 子进程关闭信号量（修正：补充信号量关闭）
        sem_close(empty);
        sem_close(full);
        sem_close(mutex);
        // 分离共享内存shmdt:shared memory detach
        shmdt(shared_buf);
        exit(EXIT_SUCCESS);
    } else {  // 父进程 - 生产者
        int item, i;
        for (i = 0; i < ITEM_COUNT; i++) {
            // 生产一个产品
            item = rand() % 100;  // 随机生成一个产品编号
           // 等待有空缓冲区（empty > 0）
            sem_wait(empty);
            // 等待获取互斥锁
            sem_wait(mutex);

            // 将产品放入缓冲区
            shared_buf->buffer[shared_buf->in] = item;
            printf("生产者: 放入产品 %d (位置 %d)\n", item, shared_buf->in);
            shared_buf->in = (shared_buf->in + 1) % BUFFER_SIZE;
            // 释放互斥锁
            sem_post(mutex);
            // 增加满缓冲区数量
            sem_post(full);
            // 模拟生产时间
            sleep(rand() % 2);
        }       
        // 等待子进程结束
        wait(NULL);
        // 清理资源
        sem_close(empty);
        sem_close(full);
        sem_close(mutex);
         // 删除命名信号量（避免残留）
        sem_unlink("/empty_sem");
        sem_unlink("/full_sem");
        sem_unlink("/mutex_sem");        
        // 分离并删除共享内存
        shmdt(shared_buf);
//Inter-Process Communication Remove ID，表示删除共享内存标识符及其//关联的共享内存区域
        shmctl(shmid, IPC_RMID, NULL);
        exit(EXIT_SUCCESS);
    }
}


